using Cairo;
using Gtk;
using Gdk;

namespace Ribbons {

	/** Ribbon group. */
	public class RibbonGroup : Bin {

		private Pango.Layout _label_layout;
		private Button _expand_button;

		private double _bar_height;
		private double _bar_width;

		private const double CHILD_PADDING = 1.0;
		private const double LINE_WIDTH = 1.0;
		private const double SPACE = 2.0;

		/** Fired whenever the expand button is clicked. */
		public signal void expanded (RibbonGroup sender);

		// XXX-GEETK: is it possible to do something when someone
		// connects/disconnects the signal?
//		{
//			add {
//				_expand_button.visible = has_expand_listener ();
//			}
//			remove {
//				_expand_button.visible = has_expand_listener ();
//			}
//		}

		/** Displayed label. */
		private string _label;
		public string label {
			set	{
				_label = value;

				if (_label == null) {
					_label_layout = null;
				} else if (_label_layout == null) {
					_label_layout = create_pango_layout (_label);
				} else {
					// XXX-GEETK: size () really needed?
					_label_layout.set_text (_label, (int) _label.size ());
				}
				queue_draw ();
			}
			get { return _label; }
		}

		/** Theme used to draw the widget. */
		private Theme _theme = new Theme ();
		public Theme theme {
			set {
				_theme = value;
				queue_draw ();
			}
			get { return _theme; }
		}

		/** Position of the label. */
		private PositionType _label_position = PositionType.BOTTOM;
		public PositionType label_position {
			set {
				_label_position = value;
				queue_draw ();
			}
			get { return _label_position; }
		}

		/** Construction method. */
		construct {
			// This is a NO_WINDOW widget => it does not have its own Gdk Window
			// => it can be transparent
			set_flags (get_flags () | WidgetFlags.NO_WINDOW);

			add_events (Gdk.EventMask.BUTTON_PRESS_MASK
					| Gdk.EventMask.BUTTON_RELEASE_MASK
					| Gdk.EventMask.POINTER_MOTION_MASK);

			this.label = null;
			this.height_request = 92;

			_expand_button = new Button.with_label ("\xe2\x87\xb2");  // U+21F2
			_expand_button.padding = 0;
			_expand_button.visible = false;
			_expand_button.set_parent (this);
			_expand_button.clicked.connect (() => {
				expanded (this);
			});
		}

		// XXX-GEETK: workaround
//		private bool has_expand_listener () {
//			var signal_id = Signal.lookup ("expanded", typeof (RibbonGroup));
//			return Signal.has_handler_pending (this, signal_id, 0, true);
//		}

		protected override void forall_internal (bool include_internals,
		                                         Gtk.Callback callback)
		{
			base.forall_internal (include_internals, callback);

			if (_expand_button != null && _expand_button.visible) {
				callback (_expand_button);
			}
		}

		protected override void size_request (out Requisition requisition) {
			base.size_request (out requisition);

			int lw, lh;

			if (_label_position == PositionType.TOP
					|| _label_position == PositionType.BOTTOM) {
				_label_layout.get_pixel_size (out lw, out lh);
			} else {
				_label_layout.get_pixel_size (out lh, out lw);
			}

			double frame_size = 2 * LINE_WIDTH + CHILD_PADDING;

			_bar_height = lh + 2 * SPACE;
			_bar_width = lw + 2 * SPACE;
				
			if (_expand_button != null && _expand_button.visible) {
				if (_label_position == PositionType.TOP
						|| _label_position == PositionType.BOTTOM) {
					_expand_button.set_size_request (lh, lh);
				} else {
					_expand_button.set_size_request (lw, lw);
				}

				Requisition req;
				_expand_button.size_request (out req);

				if (_label_position == PositionType.TOP
						|| _label_position == PositionType.BOTTOM) {
					_bar_width += _expand_button.width_request + (int) SPACE;
				} else {
					_bar_height += _expand_button.height_request + (int) SPACE;
				}
			}

			Requisition child_requisition = Requisition ();

			if (this.child != null && this.child.visible) {
				if (this.height_request != -1) {
					int left = this.height_request;
					if (_label_position == PositionType.TOP
							|| _label_position == PositionType.BOTTOM) {
						left -= (int) (2 * frame_size + _bar_height);
					} else {
						left -= (int) (2 * frame_size);
					}

					this.child.height_request = left;
				}
				if (this.width_request != -1) {
					int left = this.width_request;
					if (_label_position == PositionType.TOP
							|| _label_position == PositionType.BOTTOM) {
						left -= (int) (2 * frame_size);
					} else {
						left -= (int) (2 * frame_size + _bar_width);
					}
					this.child.width_request = left;
				}
				this.child.size_request (out child_requisition);
			}

			if (this.width_request == -1) {
				if (this.child != null && this.child.visible) {
					requisition.width = child_requisition.width + (int) (2 * frame_size);

					if (_label_position == PositionType.LEFT
							|| _label_position == PositionType.RIGHT) {
						requisition.width += (int) _bar_width;
					}
				} else {
					requisition.width = (int) (2 * frame_size + _bar_width);
				}
			}

			if (this.height_request == -1) {
				if (this.child != null && this.child.visible) {
					requisition.height = child_requisition.height + (int) (2 * frame_size);

					if (_label_position == PositionType.TOP
							|| _label_position == PositionType.BOTTOM) {
						requisition.height += (int) _bar_height;
					}
				} else {
					requisition.height = (int) (2 * frame_size + _bar_height);
				}
			}
		}

		protected override void size_allocate (Rectangle allocation) {
			base.size_allocate (allocation);

			if (_expand_button != null && _expand_button.visible) {
				double frame_size = 2 * LINE_WIDTH + SPACE;
				Rectangle r = Rectangle ();
				r.height = _expand_button.height_request;
				r.width = _expand_button.width_request;

				if (_label_position == PositionType.LEFT) {
					r.x = allocation.x + (int) frame_size;
				} else {
					r.x = allocation.x + allocation.width - r.width - (int) frame_size;
				}

				if (_label_position == PositionType.TOP) {
					r.y = allocation.y + (int) frame_size;
				} else {
					r.y = allocation.y + allocation.height - r.height - (int) frame_size;
				}

				_expand_button.size_allocate (r);
			}

			if (this.child != null && this.child.visible) {
				double frame_size = 2 * LINE_WIDTH + CHILD_PADDING;
				int wi = allocation.width - (int) (2 * frame_size);
				int he = allocation.height - (int) (2 * frame_size);

				var r = Rectangle () {
					x = allocation.x + (int) frame_size,
					y = allocation.y + (int) frame_size,
					width = wi,
					height = he
				};

				if (_label_position == PositionType.TOP) {
					r.y += (int) _bar_height;
				} else if (_label_position == PositionType.LEFT) {
					r.x += (int) _bar_width;
				}

				if (_label_position == PositionType.TOP
						|| _label_position == PositionType.BOTTOM) {
					r.height -= (int) _bar_height;
				} else {
					r.width -= (int) _bar_width;
				}

				this.child.size_allocate (r);
			}
		}

		private void draw (Context cr) {
			var rect = Rectangle () {
				x = this.allocation.x,
				y = this.allocation.y,
				width = this.allocation.width,
				height = this.allocation.height
			};
			_theme.draw_group (cr, rect, 4.0, LINE_WIDTH, SPACE, _label_layout,
			                   _expand_button, this);
		}

		protected override bool expose_event (EventExpose event) {
			var cr = Gdk.cairo_create (this.window);

			cr.rectangle (event.area.x, event.area.y,
							event.area.width, event.area.height);
			cr.clip ();
			draw (cr);

//			cr.target.dispose ();	// XXX-GEETK
//			cr.dispose ();

			return base.expose_event (event);
		}
	}
}
